From 13a341e0e9f8f99c3749161ab60424eb10815d59 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Mon, 11 Jul 2005 17:51:54 +0000 Subject: [PATCH] When dragging text, use a drag icon showing the (ellipsized) text that is 2005-07-11 Matthias Clasen When dragging text, use a drag icon showing the (ellipsized) text that is being dragged: (#161132, Kevin Duffus, patch by Carlos Garnacho Parro) * gtk/gtktextutil.h: * gtk/gtktextutil.c (_gtk_text_util_create_drag_icon): Add a function to create a pixmap for use when dragging text. * gtk/gtktextview.c (gtk_text_view_start_selection_dnd): * gtk/gtklabel.c (gtk_label_motion): * gtk/gtkentry.c (gtk_entry_motion_notify): Use a drag icon showing the text being dragged. 2005-07-11 Matthias Clasen * gtk/gtkentry.c (gtk_entry_move_forward_word) (gtk_entry_move_backward_word): Match the text view change to allow selecting whitespace with double-click. --- ChangeLog | 19 +++++++ ChangeLog.pre-2-10 | 19 +++++++ ChangeLog.pre-2-8 | 19 +++++++ gtk/gtkentry.c | 82 +++++++++++++++++++++++------- gtk/gtklabel.c | 57 +++++++++++++++++++-- gtk/gtktextutil.c | 124 +++++++++++++++++++++++++++++++++++++++++++++ gtk/gtktextutil.h | 4 ++ gtk/gtktextview.c | 52 +++++++++++++++++-- 8 files changed, 351 insertions(+), 25 deletions(-) diff --git a/ChangeLog b/ChangeLog index a73479ad30..8b557e90be 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,24 @@ 2005-07-11 Matthias Clasen + When dragging text, use a drag icon showing the (ellipsized) + text that is being dragged: (#161132, Kevin Duffus, patch + by Carlos Garnacho Parro) + + * gtk/gtktextutil.h: + * gtk/gtktextutil.c (_gtk_text_util_create_drag_icon): Add + a function to create a pixmap for use when dragging text. + + * gtk/gtktextview.c (gtk_text_view_start_selection_dnd): + * gtk/gtklabel.c (gtk_label_motion): + * gtk/gtkentry.c (gtk_entry_motion_notify): Use a drag icon + showing the text being dragged. + +2005-07-11 Matthias Clasen + + * gtk/gtkentry.c (gtk_entry_move_forward_word) + (gtk_entry_move_backward_word): Match the text view change + to allow selecting whitespace with double-click. + * gtk/gtktextview.c (extend_selection): Make double-clicking between words select whitespace. (#309860, Mike Miller, patch by Paolo Borelli) diff --git a/ChangeLog.pre-2-10 b/ChangeLog.pre-2-10 index a73479ad30..8b557e90be 100644 --- a/ChangeLog.pre-2-10 +++ b/ChangeLog.pre-2-10 @@ -1,5 +1,24 @@ 2005-07-11 Matthias Clasen + When dragging text, use a drag icon showing the (ellipsized) + text that is being dragged: (#161132, Kevin Duffus, patch + by Carlos Garnacho Parro) + + * gtk/gtktextutil.h: + * gtk/gtktextutil.c (_gtk_text_util_create_drag_icon): Add + a function to create a pixmap for use when dragging text. + + * gtk/gtktextview.c (gtk_text_view_start_selection_dnd): + * gtk/gtklabel.c (gtk_label_motion): + * gtk/gtkentry.c (gtk_entry_motion_notify): Use a drag icon + showing the text being dragged. + +2005-07-11 Matthias Clasen + + * gtk/gtkentry.c (gtk_entry_move_forward_word) + (gtk_entry_move_backward_word): Match the text view change + to allow selecting whitespace with double-click. + * gtk/gtktextview.c (extend_selection): Make double-clicking between words select whitespace. (#309860, Mike Miller, patch by Paolo Borelli) diff --git a/ChangeLog.pre-2-8 b/ChangeLog.pre-2-8 index a73479ad30..8b557e90be 100644 --- a/ChangeLog.pre-2-8 +++ b/ChangeLog.pre-2-8 @@ -1,5 +1,24 @@ 2005-07-11 Matthias Clasen + When dragging text, use a drag icon showing the (ellipsized) + text that is being dragged: (#161132, Kevin Duffus, patch + by Carlos Garnacho Parro) + + * gtk/gtktextutil.h: + * gtk/gtktextutil.c (_gtk_text_util_create_drag_icon): Add + a function to create a pixmap for use when dragging text. + + * gtk/gtktextview.c (gtk_text_view_start_selection_dnd): + * gtk/gtklabel.c (gtk_label_motion): + * gtk/gtkentry.c (gtk_entry_motion_notify): Use a drag icon + showing the text being dragged. + +2005-07-11 Matthias Clasen + + * gtk/gtkentry.c (gtk_entry_move_forward_word) + (gtk_entry_move_backward_word): Match the text view change + to allow selecting whitespace with double-click. + * gtk/gtktextview.c (extend_selection): Make double-clicking between words select whitespace. (#309860, Mike Miller, patch by Paolo Borelli) diff --git a/gtk/gtkentry.c b/gtk/gtkentry.c index f44e75c20b..748873ee39 100644 --- a/gtk/gtkentry.c +++ b/gtk/gtkentry.c @@ -1,3 +1,4 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* GTK - The GIMP Toolkit * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald * @@ -1649,6 +1650,49 @@ gtk_entry_button_release (GtkWidget *widget, return TRUE; } +static gchar * +_gtk_entry_get_selected_text (GtkEntry *entry) +{ + GtkEditable *editable = GTK_EDITABLE (entry); + gint start_text, end_text; + gchar *text = NULL; + + if (gtk_editable_get_selection_bounds (editable, &start_text, &end_text)) + text = gtk_editable_get_chars (editable, start_text, end_text); + + return text; +} + +static void +drag_begin_cb (GtkWidget *widget, + GdkDragContext *context, + gpointer data) +{ + GtkEntry *entry; + gchar *text; + GdkPixmap *pixmap = NULL; + + g_signal_handlers_disconnect_by_func (widget, drag_begin_cb, NULL); + + entry = GTK_ENTRY (widget); + + text = _gtk_entry_get_selected_text (entry); + pixmap = _gtk_text_util_create_drag_icon (widget, text, -1); + + if (entry->visible && pixmap) + gtk_drag_set_icon_pixmap (context, + gdk_drawable_get_colormap (pixmap), + pixmap, + NULL, + -2, -2); + else + gtk_drag_set_icon_default (context); + + if (pixmap) + g_object_unref (pixmap); + g_free (text); +} + static gint gtk_entry_motion_notify (GtkWidget *widget, GdkEventMotion *event) @@ -1678,25 +1722,25 @@ gtk_entry_motion_notify (GtkWidget *widget, if (entry->in_drag) { if (gtk_drag_check_threshold (widget, - entry->drag_start_x, entry->drag_start_y, - event->x + entry->scroll_offset, event->y)) - { - GdkDragContext *context; - GtkTargetList *target_list = gtk_target_list_new (NULL, 0); - guint actions = entry->editable ? GDK_ACTION_COPY | GDK_ACTION_MOVE : GDK_ACTION_COPY; + entry->drag_start_x, entry->drag_start_y, + event->x + entry->scroll_offset, event->y)) + { + GdkDragContext *context; + GtkTargetList *target_list = gtk_target_list_new (NULL, 0); + guint actions = entry->editable ? GDK_ACTION_COPY | GDK_ACTION_MOVE : GDK_ACTION_COPY; - gtk_target_list_add_text_targets (target_list, 0); + gtk_target_list_add_text_targets (target_list, 0); - context = gtk_drag_begin (widget, target_list, actions, - entry->button, (GdkEvent *)event); + g_signal_connect (widget, "drag-begin", + G_CALLBACK (drag_begin_cb), NULL); + context = gtk_drag_begin (widget, target_list, actions, + entry->button, (GdkEvent *)event); + entry->in_drag = FALSE; + entry->button = 0; - entry->in_drag = FALSE; - entry->button = 0; - - gtk_target_list_unref (target_list); - gtk_drag_set_icon_default (context); - } + gtk_target_list_unref (target_list); + } } else { @@ -3549,9 +3593,9 @@ gtk_entry_move_forward_word (GtkEntry *entry, pango_layout_get_log_attrs (layout, &log_attrs, &n_attrs); - /* Find the next word end */ + /* Find the next word boundary */ new_pos++; - while (new_pos < n_attrs && !log_attrs[new_pos].is_word_end) + while (new_pos < n_attrs && !(log_attrs[new_pos].is_word_start || log_attrs[new_pos].is_word_end)) new_pos++; g_free (log_attrs); @@ -3582,8 +3626,8 @@ gtk_entry_move_backward_word (GtkEntry *entry, new_pos = start - 1; - /* Find the previous word beginning */ - while (new_pos > 0 && !log_attrs[new_pos].is_word_start) + /* Find the previous word boundary */ + while (new_pos > 0 && !(log_attrs[new_pos].is_word_start || log_attrs[new_pos].is_word_end)) new_pos--; g_free (log_attrs); diff --git a/gtk/gtklabel.c b/gtk/gtklabel.c index df890c9b80..460285a0be 100644 --- a/gtk/gtklabel.c +++ b/gtk/gtklabel.c @@ -37,6 +37,7 @@ #include "gtkimagemenuitem.h" #include "gtkintl.h" #include "gtkseparatormenuitem.h" +#include "gtktextutil.h" #include "gtkmenuitem.h" #include "gtknotebook.h" #include "gtkstock.h" @@ -2832,6 +2833,56 @@ gtk_label_button_release (GtkWidget *widget, return TRUE; } +static void +drag_begin_cb (GtkWidget *widget, + GdkDragContext *context, + gpointer data) +{ + GtkLabel *label; + GdkPixmap *pixmap = NULL; + + g_signal_handlers_disconnect_by_func (widget, drag_begin_cb, NULL); + + label = GTK_LABEL (widget); + + if ((label->select_info->selection_anchor != + label->select_info->selection_end) && + label->text) + { + gint start, end; + gint len; + + start = MIN (label->select_info->selection_anchor, + label->select_info->selection_end); + end = MAX (label->select_info->selection_anchor, + label->select_info->selection_end); + + len = strlen (label->text); + + if (end > len) + end = len; + + if (start > len) + start = len; + + pixmap = _gtk_text_util_create_drag_icon (widget, + label->text + start, + end - start); + } + + if (pixmap) + gtk_drag_set_icon_pixmap (context, + gdk_drawable_get_colormap (pixmap), + pixmap, + NULL, + -2, -2); + else + gtk_drag_set_icon_default (context); + + if (pixmap) + g_object_unref (pixmap); +} + static gboolean gtk_label_motion (GtkWidget *widget, GdkEventMotion *event) @@ -2863,16 +2914,16 @@ gtk_label_motion (GtkWidget *widget, GtkTargetList *target_list = gtk_target_list_new (NULL, 0); gtk_target_list_add_text_targets (target_list, 0); - + + g_signal_connect (widget, "drag-begin", + G_CALLBACK (drag_begin_cb), NULL); context = gtk_drag_begin (widget, target_list, GDK_ACTION_COPY, 1, (GdkEvent *)event); - label->select_info->in_drag = FALSE; gtk_target_list_unref (target_list); - gtk_drag_set_icon_default (context); } } else diff --git a/gtk/gtktextutil.c b/gtk/gtktextutil.c index 9d72c18646..e9f535c7c3 100644 --- a/gtk/gtktextutil.c +++ b/gtk/gtktextutil.c @@ -1,3 +1,4 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* GTK - The GIMP Toolkit * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald * @@ -30,6 +31,11 @@ #include "gtkmenuitem.h" #include "gtkalias.h" +#define DRAG_ICON_MAX_WIDTH 250 +#define DRAG_ICON_LAYOUT_BORDER 2 +#define DRAG_ICON_MAX_LINES 7 +#define ELLIPSIS_CHARACTER "\xe2\x80\xa6" + typedef struct _GtkUnicodeMenuEntry GtkUnicodeMenuEntry; typedef struct _GtkTextUtilCallbackInfo GtkTextUtilCallbackInfo; @@ -117,3 +123,121 @@ _gtk_text_util_append_special_char_menuitems (GtkMenuShell *menushe } } +static void +append_n_lines (GString *str, const gchar *text, GSList *lines, gint n_lines) +{ + PangoLayoutLine *line; + gint i; + + for (i = 0; i < n_lines; i++) + { + line = lines->data; + g_string_append_len (str, &text[line->start_index], line->length); + lines = lines->next; + } +} + +static void +limit_layout_lines (PangoLayout *layout) +{ + const gchar *text; + GString *str; + GSList *lines, *elem; + gint n_lines; + + n_lines = pango_layout_get_line_count (layout); + + if (n_lines >= DRAG_ICON_MAX_LINES) + { + text = pango_layout_get_text (layout); + str = g_string_new (NULL); + lines = pango_layout_get_lines (layout); + + /* get first lines */ + elem = lines; + append_n_lines (str, text, elem, + DRAG_ICON_MAX_LINES / 2); + + g_string_append (str, "\n" ELLIPSIS_CHARACTER "\n"); + + /* get last lines */ + elem = g_slist_nth (lines, n_lines - DRAG_ICON_MAX_LINES / 2); + append_n_lines (str, text, elem, + DRAG_ICON_MAX_LINES / 2); + + pango_layout_set_text (layout, str->str, -1); + g_string_free (str, TRUE); + } +} + +/** + * _gtk_text_util_create_drag_icon + * @widget: #GtkWidget to extract the pango context + * @text: a #gchar to render the icon + * @len: length of @text, or -1 for NUL-terminated text + * + * Creates a drag and drop icon from @text. + **/ +GdkPixmap* +_gtk_text_util_create_drag_icon (GtkWidget *widget, + gchar *text, + gsize len) +{ + GdkDrawable *drawable = NULL; + PangoContext *context; + PangoLayout *layout; + gint pixmap_height, pixmap_width; + gint layout_width, layout_height; + gint n_lines; + + g_return_val_if_fail (widget != NULL, NULL); + g_return_val_if_fail (text != NULL, NULL); + + context = gtk_widget_get_pango_context (widget); + layout = pango_layout_new (context); + + pango_layout_set_text (layout, text, len); + pango_layout_set_wrap (layout, PANGO_WRAP_WORD_CHAR); + pango_layout_get_size (layout, &layout_width, &layout_height); + + layout_width = MIN (layout_width, DRAG_ICON_MAX_WIDTH * PANGO_SCALE); + pango_layout_set_width (layout, layout_width); + n_lines = pango_layout_get_line_count (layout); + + limit_layout_lines (layout); + + /* get again layout extents, they may have changed */ + pango_layout_get_size (layout, &layout_width, &layout_height); + + pixmap_width = layout_width / PANGO_SCALE + DRAG_ICON_LAYOUT_BORDER * 2; + pixmap_height = layout_height / PANGO_SCALE + DRAG_ICON_LAYOUT_BORDER * 2; + + drawable = gdk_pixmap_new (widget->window, + pixmap_width + 2, + pixmap_height + 2, + -1); + + gdk_draw_rectangle (drawable, + widget->style->base_gc [GTK_WIDGET_STATE (widget)], + TRUE, + 0, 0, + pixmap_width + 1, + pixmap_height + 1); + + gdk_draw_layout (drawable, + widget->style->text_gc [GTK_WIDGET_STATE (widget)], + 1 + DRAG_ICON_LAYOUT_BORDER, + 1 + DRAG_ICON_LAYOUT_BORDER, + layout); + + gdk_draw_rectangle (drawable, + widget->style->black_gc, + FALSE, + 0, 0, + pixmap_width + 1, + pixmap_height + 1); + + g_object_unref (layout); + + return drawable; +} diff --git a/gtk/gtktextutil.h b/gtk/gtktextutil.h index 2a9b6d3b2f..27b900e435 100644 --- a/gtk/gtktextutil.h +++ b/gtk/gtktextutil.h @@ -30,6 +30,7 @@ #include #include +#include G_BEGIN_DECLS @@ -46,5 +47,8 @@ void _gtk_text_util_append_special_char_menuitems (GtkMenuShell *me G_END_DECLS +GdkPixmap* _gtk_text_util_create_drag_icon (GtkWidget *widget, + gchar *text, + gsize len); #endif /* __GTK_TEXT_UTIL_H__ */ diff --git a/gtk/gtktextview.c b/gtk/gtktextview.c index 9a1aa73007..0b5c7322c5 100644 --- a/gtk/gtktextview.c +++ b/gtk/gtktextview.c @@ -1,3 +1,4 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* GTK - The GIMP Toolkit * gtktextview.c Copyright (C) 2000 Red Hat, Inc. * @@ -5977,17 +5978,62 @@ gtk_text_view_reset_im_context (GtkTextView *text_view) } } +static gchar* +_gtk_text_view_get_selected_text (GtkTextView *text_view) +{ + GtkTextBuffer *buffer; + GtkTextIter start, end; + gchar *text = NULL; + + buffer = gtk_text_view_get_buffer (text_view); + + if (gtk_text_buffer_get_selection_bounds (buffer, &start, &end)) + text = gtk_text_buffer_get_text (buffer, &start, &end, FALSE); + + return text; +} + /* * DND feature */ +static void +drag_begin_cb (GtkWidget *widget, + GdkDragContext *context, + gpointer data) +{ + GtkTextView *text_view; + gchar *text; + GdkPixmap *pixmap = NULL; + + g_signal_handlers_disconnect_by_func (widget, drag_begin_cb, NULL); + + text_view = GTK_TEXT_VIEW (widget); + + text = _gtk_text_view_get_selected_text (text_view); + pixmap = _gtk_text_util_create_drag_icon (widget, text, -1); + + if (pixmap) + gtk_drag_set_icon_pixmap (context, + gdk_drawable_get_colormap (pixmap), + pixmap, + NULL, + -2, -2); + else + gtk_drag_set_icon_default (context); + + if (pixmap) + g_object_unref (pixmap); + g_free (text); +} + static void gtk_text_view_start_selection_dnd (GtkTextView *text_view, const GtkTextIter *iter, GdkEventMotion *event) { GdkDragContext *context; - GtkTargetList *target_list; + GtkTargetList *target_list; text_view->drag_start_x = -1; text_view->drag_start_y = -1; @@ -5997,13 +6043,13 @@ gtk_text_view_start_selection_dnd (GtkTextView *text_view, G_N_ELEMENTS (target_table)); gtk_target_list_add_text_targets (target_list, 0); + g_signal_connect (text_view, "drag-begin", + G_CALLBACK (drag_begin_cb), NULL); context = gtk_drag_begin (GTK_WIDGET (text_view), target_list, GDK_ACTION_COPY | GDK_ACTION_MOVE, 1, (GdkEvent*)event); gtk_target_list_unref (target_list); - - gtk_drag_set_icon_default (context); } static void -- 2.30.2